package com.sheledon.flyCat.core.configuration;

import com.sheledon.flyCat.annotation.ioc.Bean;
import com.sheledon.flyCat.annotation.ioc.Configuration;
import com.sheledon.flyCat.core.configuration.factory.ConfigurationFileResolverFactory;
import com.sheledon.flyCat.core.configuration.resolver.Resolver;
import com.sheledon.flyCat.core.exception.BeanCreationException;
import com.sheledon.flyCat.core.ioc.BeanFactory;
import com.sheledon.flyCat.core.ioc.SingletonRegister;
import com.sheledon.flyCat.factory.ClassFactory;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Set;

/**
 * @author sheledon
 */
public class ConfigurationManager {
    private final static HashMap<String,Object> CONFIGURATION= new HashMap<>();
    //用来解决 configuration配置类中构建bean的时候相互依赖的问题
    public final static HashMap<String,Method> BEAN_METHOD = new HashMap<>();

    private final static String [] DEFAULT_CONFIGURATION_NAME = {
            "application.yml","application.yaml","application.properties"};

    public static void loadConfigurationFile(Class<?> clazz){
        ClassLoader classLoader = clazz.getClassLoader();
        for (String s : DEFAULT_CONFIGURATION_NAME) {
            InputStream resourceAsStream = classLoader.getResourceAsStream(s);
            if (resourceAsStream!=null){
                Resolver resolver = ConfigurationFileResolverFactory.getResolver(s);
                HashMap<String, Object> result = resolver.resolve(resourceAsStream);
                CONFIGURATION.putAll(result);
            }
        }
    }
    /**
     * 实现的关键:
     * 当要构建bean的时候，
     * 对于方法参数，
     * 首先应该扫描其他的configuration中的bean
     * 如果没有，再去扫描bean工厂
     */
    public static void loadConfiguration(){
        ClassFactory.CLASS_FACTORY.get(Configuration.class).forEach(BeanFactory::loadBean);
        scanBean();
        initializeBean();
    }
    private static void scanBean(){
        Set<Class<?>> classes = ClassFactory.CLASS_FACTORY.get(Bean.class);
        ClassFactory.CLASS_FACTORY.get(Configuration.class).forEach(clazz->{
            for (Method method : clazz.getDeclaredMethods()) {
                if (method.isAnnotationPresent(Bean.class)){
                    classes.add(method.getReturnType());
                    String beanName = getBeanName(method);
                    if (BEAN_METHOD.containsKey(beanName)){
                        throw new BeanCreationException("configuration bean already exists : "+beanName);
                    }
                    BEAN_METHOD.put(beanName,method);
                }
            }
        });
    }
    private static void initializeBean(){
        BEAN_METHOD.forEach((name,method)->{
            Object targetObject = BeanFactory.getBean(method.getDeclaringClass().getSimpleName());
            Object bean = SingletonRegister.registerSingleton(name,method,targetObject);
            BeanFactory.addBean(name,bean);
        });
    }
    private static String getBeanName(Method method){
        Bean annotation = method.getAnnotation(Bean.class);
        String name = annotation.name();
        if ("".equals(name)){
            name = method.getName();
        }
        return name;
    }
    public static boolean getBoolean(String name){
        return (boolean) CONFIGURATION.get(name);
    }
    public static int getInt(String name){
        return (int) CONFIGURATION.get(name);
    }
    public static String getString(String name){
        return (String) CONFIGURATION.get(name);
    }
    public static Object get(String name){
        return CONFIGURATION.get(name);
    }
}
